import './index.less';
import { nanoid } from 'nanoid';
import clsx from 'clsx';
import Box from '@mui/material/Box';
import { styled } from '@mui/material/styles';
import ErrorOutlineIcon from '@mui/icons-material/ErrorOutline';
import IconButton from '@mui/material/IconButton';
import CircularProgress from '@mui/material/CircularProgress';
import Image from 'rc-image';
import removeImg from '@/assets/images/remove.png';
import {
  AddIcon,
  CloseOutlined,
  LeftOutlined,
  RightOutlined,
  RotateLeftOutlined,
  RotateRightOutlined,
  SwapOutlined,
  ZoomInOutlined,
  ZoomOutOutlined,
} from '../icons';
import { message } from '../message';
import 'rc-image/assets/index.css';
import { useControlledState } from '@/hooks';

export type IUploadProps = {
  className?: string;
  /** 上传地址 */
  action?: string;
  /** 接受上传的文件类型，详见 https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#accept */
  accept?: string;
  /**  通过覆盖默认的上传行为，可以自定义自己的上传实现*/
  customRequest?: VoidFunction;
  /**上传所需额外参数或返回上传额外参数的方法 */
  data?: Record<string, any> | ((file: File) => Promise<Record<string, any>>);
  /** 默认已经上传的文件列表 */
  defaultFileList?: Omit<IFileRecord, 'file'>[];
  /** 已经上传的文件列表（受控）*/
  fileList?: IFileRecord[];
  /** 设置上传请求头 */
  headers?: Record<string, any>;
  /** 是否支持多选文件，ie10+ 支持。开启后按住 ctrl 可选择多个文件 */
  multiple?: boolean;
  /** 发到后台的文件参数名 */
  name?: string;
  /** 自定义文件预览逻辑 */
  previewFile?: (file: File | Blob) => Promise<string>;
  /** 上传请求时是否携带 cookie */
  withCredentials?: boolean;
  /** 上传请求的 http method */
  method?: string;
  /** 限制上传数量。当为 1 时，始终用最新上传的文件代替当前文件 */
  maxCount?: number;
  /** 上传文件改变时的回调，上传每个阶段都会触发该事件 */
  onChange?: (fileList?: IFileRecord[]) => void;
  /** 点击文件链接或预览图标时的回调 */
  onPreview?: (params: { file: IFileRecord }) => void;
  /** 点击移除文件时的回调，返回值为 false 时不移除。支持返回一个 Promise 对象，Promise 对象 resolve(false) 或 reject 时不移除 */
  onRemove?: () => boolean | Promise<boolean>;
};

export type IFileRecord = {
  uid: string;
  name: string;
  file?: File;
  status: 'uploading' | 'done' | 'error' | 'removed';
  /** 缩略图地址 */
  thumbUrl: string;
  /** 预览地址 */
  previewUrl: string;
  /** 下载地址 */
  url: string;
  /** 上传进度 */
  progress?: number;
};

const icons = {
  rotateLeft: <RotateLeftOutlined />,
  rotateRight: <RotateRightOutlined />,
  zoomIn: <ZoomInOutlined />,
  zoomOut: <ZoomOutOutlined />,
  close: <CloseOutlined />,
  left: <LeftOutlined />,
  right: <RightOutlined />,
  flipX: <SwapOutlined />,
  flipY: <SwapOutlined rotate={90} />,
};

const VisuallyHiddenInput = styled('input')({
  clip: 'rect(0 0 0 0)',
  clipPath: 'inset(50%)',
  height: 1,
  overflow: 'hidden',
  position: 'absolute',
  bottom: 0,
  left: 0,
  whiteSpace: 'nowrap',
  width: 1,
});

const urlMap: Record<string, any> = {};

export const genFileRecord = (file: File, options?: Pick<IFileRecord, 'uid'>) => {
  const url = URL.createObjectURL(file);
  const record: IFileRecord = {
    uid: options?.uid ?? nanoid(),
    name: file.name,
    file,
    status: 'done',
    thumbUrl: url,
    url,
    previewUrl: url,
  };
  urlMap[record.uid] = url;
  return record;
};

export const Upload = (props: IUploadProps) => {
  const { className, accept = 'image/*', multiple, defaultFileList, maxCount, onChange: propOnChange } = props;
  const [fileList, setFileList] = useControlledState<IFileRecord[]>(props.fileList ?? defaultFileList, propOnChange);

  const verify = (files: FileList) => {
    if (maxCount) {
      if (maxCount === 1) {
        if (files.length > 1) {
          message.warning({ content: '只能上传一个文件' });
          return false;
        }
      } else if (maxCount < fileList.length + files.length) {
        message.warning({ content: `最多上传${maxCount}个文件` });
        return false;
      }
    }
    if (accept) {
      const acceptList = accept.split(',');
      for (let i = 0; i < files.length; i++) {
        if (acceptList.every((type) => !new RegExp(type).test(files[i].type))) {
          message.warning({ content: `文件类型不支持，仅支持${acceptList.join('、')}格式` });
          return false;
        }
      }
    }
    return true;
  };
  const onErrorClick = (record: IFileRecord) => {
    console.log(record);
  };

  const renderByStatus = (record: IFileRecord) => {
    if (record.status === 'uploading') {
      return (
        <Box className="smap-upload-status smap-upload-loading">
          <CircularProgress variant="determinate" sx={{ color: (theme) => theme.palette.grey[500] }} size={30} thickness={4} value={100} />
          <CircularProgress
            sx={{ position: 'absolute', color: '#fff' }}
            variant="determinate"
            thickness={4}
            size={30}
            value={record.progress}
          />
        </Box>
      );
    }
    if (record.status === 'error') {
      return (
        <Box className="smap-upload-status smap-upload-error" onClick={() => onErrorClick(record)}>
          <ErrorOutlineIcon />
        </Box>
      );
    }
    return null;
  };

  const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const { files } = e.target;
    if (!files?.length) return message.warning({ content: '请选择文件' });
    if (!verify(files)) return;
    let newFileList = [...fileList];
    if (maxCount === 1) {
      newFileList = [genFileRecord(files[0])];
    } else {
      newFileList.push(...Array.from(files).map((v) => genFileRecord(v)));
    }
    setFileList(newFileList);
    e.target.value = '';
  };

  const onRemoveClick = (record: IFileRecord) => {
    setFileList(
      fileList.filter((item) => {
        const flag = item.uid !== record.uid;
        if (!flag && urlMap[item.uid]) URL.revokeObjectURL(urlMap[item.uid]);
        return flag;
      }),
    );
  };

  return (
    <Box
      className={clsx(className, 'smap-upload')}
      sx={{
        '.MuiIconButton-root': { width: 48, height: 48, bgcolor: '#f6f7f8', borderRadius: '6px' },
        display: 'flex',
        gap: 0.5,
      }}
    >
      <Image.PreviewGroup icons={icons}>
        {fileList
          .filter((v) => v.status !== 'removed')
          .map((record) => (
            <div key={record.uid} className="smap-upload-card">
              {record.status !== 'uploading' && (
                <img className="smap-upload-remove" src={removeImg} onClick={() => onRemoveClick(record)} />
              )}
              <Image src={record.thumbUrl} preview={{ mask: <ZoomInOutlined /> }} />
              {renderByStatus(record)}
            </div>
          ))}
      </Image.PreviewGroup>
      {!maxCount || !fileList.length || maxCount === 1 || maxCount > fileList.length ? (
        <IconButton component="label">
          <AddIcon />
          <VisuallyHiddenInput accept={accept} multiple={multiple} type="file" onChange={onChange} />
        </IconButton>
      ) : null}
    </Box>
  );
};
